//
// Ephi - simulation of magnetic fields and particles
// Copyright (C) 2007 Indrek Mandre <indrek(at)mare.ee>
// For more information please see http://www.mare.ee/indrek/ephi/
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//

// Polywell simulation. Simulate the movement of a single electron
// inside the polywell magnetic containment system.

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <list>
#include <vector>
#include <deque>

#include "ephi.hpp"

struct screen_entry
{
  screen_entry () { screen = 0; }
  ~screen_entry () { delete screen; }
  Screen *screen;
  std::string path;
  bool draw_axes;
  bool draw_statics;
  prec_t hscale;
  prec_t fov;
  vect3d look_at;
  vect3d look_from;
  vect3d look_up;
  vect3d normal;
  vect3d right, up;

  size_t pathlimit;
  size_t flushlimit;

  size_t rotate_period;
  vect3d rotate_center;

  vect3d rotate_normal;

  size_t flushcount;
  size_t frameno;
  typedef std::pair<vect3d, Screen::color> pathentry_t;
  typedef std::deque<pathentry_t> pathlog_t;
  typedef std::list<pathlog_t > pathlogs_t;
  pathlogs_t pathlogs;
};

struct inject_entry
{
  prec_t energy;
  vect3d pos;
  vect3d dir;
  prec_t charge;
  prec_t mass;
  size_t injections;
  size_t interval;
  prec_t spread;
  size_t count;
  size_t start;

  vect3d color_rgb;
  vect3d color_center;
  prec_t color_radius;
  vect3d color_center_rgb;
  vect3d color_edge_rgb;
  size_t color_new_age;
  vect3d color_new_rgb;
};

struct pos_entry
{
  vect3d pos;
  inject_entry *ie;
  size_t injected_at;
};

static Screen::color get_color (pos_entry& pe, size_t current_sample)
{
  inject_entry& ie = *pe.ie;
  if ( ie.color_new_age != 0 && current_sample - pe.injected_at <= ie.color_new_age )
      return ie.color_new_rgb;
  if ( ie.color_radius == 0.0 )
      return ie.color_rgb;
  prec_t dist = (pe.pos - ie.color_center).length();
  if ( dist > ie.color_radius )
      dist = ie.color_radius;
  prec_t f = dist / ie.color_radius;
  return ie.color_center_rgb * (1 - f) + f * ie.color_edge_rgb;
}

void usage (char *name)
{
  fprintf (stderr, "%s: %s [-c config]\n", name, name);
  exit(-1);
}

int main (int argc, char *argv[])
{
  const char *configpath = "polywell.txt";

  for ( int j = 1; j < argc; j++ )
    {
      if ( argv[j][0] != '-' )
          usage(argv[0]);

      switch (argv[j][1])
        {
          case 'c': {
            j++;
            if ( j == argc )
                usage(argv[0]);
            configpath = argv[j];
            break;
          }
          default: {
            usage(argv[0]);
            break;
          }
        }
    }

  Config cfg;
  cfg.reg_enum ("polywell", "polywell.configuration", "cube", "cube", "dodec", NULL);
  cfg.reg_str ("polywell", "polywell.coildata", "");
  cfg.reg_num ("polywell", "polywell.radius", 0.3);
  cfg.reg_num ("polywell", "polywell.thickness", 0.08);
  cfg.reg_num ("polywell", "polywell.spacing", 0.005);
  cfg.reg_num ("polywell", "polywell.current", 108000);
  cfg.reg_num ("polywell", "polywell.charge_per_meter", 3e-07);

  cfg.reg_num ("simulation", "simulation.time", 0.000002);
  cfg.reg_num ("simulation", "simulation.step", 1.0);
  cfg.reg_num ("simulation", "simulation.accuracy", 0.0001);
  cfg.reg_num ("simulation", "simulation.sample", 0.00000000001);
  cfg.reg_num ("simulation", "simulation.flush_interval", 20);

  cfg.reg_str ("screen", "screen.path", "");
  cfg.reg_num ("screen", "screen.width", 800.0);
  cfg.reg_num ("screen", "screen.height", 800.0);
  cfg.reg_num ("screen", "screen.hscale", 1.25);
  cfg.reg_bool ("screen", "screen.draw.axes", false);
  cfg.reg_bool ("screen", "screen.draw.statics", false);
  cfg.reg_bool ("screen", "screen.zbuffer", false);
  cfg.reg_num ("screen", "screen.pathlimit", 0);
  cfg.reg_num ("screen", "screen.flushlimit", 0);

  cfg.reg_num ("screen", "screen.fov", 0);
  cfg.reg_vect3d ("screen", "screen.look_from", vect3d(0, 0, 1));
  cfg.reg_vect3d ("screen", "screen.look_up", vect3d(0, 1, 0));
  cfg.reg_vect3d ("screen", "screen.look_at", vect3d(0, 0, 0));

  cfg.reg_num ("screen", "screen.rotate.period", 0);
  cfg.reg_vect3d ("screen", "screen.rotate.center", vect3d(0, 0, 0));

  cfg.reg_num ("inject", "inject.energy", 10.0);
  cfg.reg_vect3d ("inject", "inject.position", vect3d(0, 0, 0));
  cfg.reg_vect3d ("inject", "inject.direction", vect3d(0, 0, 1));
  cfg.reg_num ("inject", "inject.mass", ELECTRON_MASS);
  cfg.reg_num ("inject", "inject.charge", -ELEMENTARY_CHARGE);
  cfg.reg_enum ("inject", "inject.particle", "electron", "electron", "custom", NULL);
  cfg.reg_num ("inject", "inject.injections", 1);
  cfg.reg_num ("inject", "inject.interval", 0);
  cfg.reg_num ("inject", "inject.spread", 0);
  cfg.reg_num ("inject", "inject.count", 1);
  cfg.reg_num ("inject", "inject.start", 0);

  cfg.reg_vect3d ("inject", "inject.coloring.rgb", vect3d(1, 0, 0));
  cfg.reg_vect3d ("inject", "inject.coloring.center", vect3d(0, 0, 0));
  cfg.reg_num ("inject", "inject.coloring.radius", 0);
  cfg.reg_vect3d ("inject", "inject.coloring.center.rgb", vect3d(1, 0, 0));
  cfg.reg_vect3d ("inject", "inject.coloring.edge.rgb", vect3d(0, 1, 0));
  cfg.reg_num ("inject", "inject.coloring.new.age", 0);
  cfg.reg_vect3d ("inject", "inject.coloring.new.rgb", vect3d(0, 1, 0));

  cfg.load (configpath);

  prec_t time_to_run = cfg.get_prec ("simulation.time");
  prec_t accuracy = cfg.get_prec ("simulation.accuracy");
  prec_t step = cfg.get_prec ("simulation.step");
  prec_t sample_interval = cfg.get_prec ("simulation.sample");
  prec_t flush_interval = cfg.get_prec ("simulation.flush_interval");

  fprintf (stderr, "Configuration:\n");
  fprintf (stderr, "  Time to run (-t): %f s\n", prec2double(time_to_run));
  fprintf (stderr, "  Integration accuracy modifier: %f\n", prec2double(accuracy));
  fprintf (stderr, "  Timestep ratio: %g\n", prec2double(step));
  fprintf (stderr, "  Sampling interval: %g\n", prec2double(sample_interval));
  fprintf (stderr, "  Flush interval: %f\n", prec2double(flush_interval));

  Statics statics;
  ElectroDynamics dyn(statics);
  CoilData cd;
  dyn.setDTM (accuracy, step);
  dyn.setDisableFields (true);

  size_t i;
  for ( i = 0; cfg.select_group ("polywell", i); i++ )
    {
      const char *conf = cfg.get_enum ("polywell.configuration");
      const char *coildata = cfg.get_str ("polywell.coildata");
      prec_t radius = cfg.get_prec ("polywell.radius");
      prec_t current = cfg.get_prec ("polywell.current");
      prec_t magnet_wire_radius = cfg.get_prec ("polywell.thickness") / 2;
      prec_t magnet_spacing = cfg.get_prec ("polywell.spacing");
      prec_t charge_per_meter = cfg.get_prec ("polywell.charge_per_meter");
      fprintf (stderr, "Creating a polywell of configuration '%s'\n", conf);
      fprintf (stderr, "  Radius of a single magnet (-r): %f m\n", prec2double(radius));
      fprintf (stderr, "  Radius of the magnet's wire (-w): %f m\n", prec2double(magnet_wire_radius));
      fprintf (stderr, "  Magnets spacing at edges (-l): %f m\n", prec2double(magnet_spacing));
      fprintf (stderr, "  Current in magnets (-i): %f A\n", prec2double(current));
      fprintf (stderr, "  Charge per meter in the grid coils (-q): %g C\n", prec2double(charge_per_meter));
      if ( *coildata )
          fprintf (stderr, "  Use precalculated coil data from %s\n", coildata);
      if ( strcmp (conf, "cube") == 0 )
        {
          if ( *coildata )
            {
              if ( !cd.load (coildata) )
                {
                  fprintf (stderr, "Unable to load coil data from '%s'\n", coildata);
                  exit(-1);
                }
              printf ("Trying to make the coils, we hope\n");
              if ( cd.cubic_bmapemap )
                {
                  printf ("Using cubic coil interpolation\n");
                  make_polywell_cube (statics, radius, magnet_wire_radius, magnet_spacing, current, charge_per_meter,
                      CoilDataFactory<StaticCICU>(cd));
                }
              else
                {
                  make_polywell_cube (statics, radius, magnet_wire_radius, magnet_spacing, current, charge_per_meter,
                      CoilDataFactory<StaticCIL>(cd));
                }
            }
          else
            {
              make_polywell_cube (statics, radius, magnet_wire_radius, magnet_spacing, current, charge_per_meter);
            }
          vect3d bf;
          dyn.getBField (vect3d(-radius, 0, 0), bf);
          fprintf (stderr, "Cube polywell coil center B field strength: %g T\n", prec2double(bf.length()));
        }
      else
          make_polywell_dodecahedron (statics, radius, magnet_wire_radius, magnet_spacing, current, charge_per_meter);
    }

  typedef std::list<screen_entry> screens_t;
  screens_t screens;
  for ( i = 0; cfg.select_group ("screen", i); i++ )
    {
      screens.push_back (screen_entry());
      screen_entry& se = screens.back();
      se.path = cfg.get_str ("screen.path");
      size_t w = cfg.get_size ("screen.width");
      size_t h = cfg.get_size ("screen.height");
      se.hscale = cfg.get_prec ("screen.hscale");

      se.fov = cfg.get_prec ("screen.fov");
      cfg.get_vect3d ("screen.look_at", se.look_at);
      cfg.get_vect3d ("screen.look_from", se.look_from);
      cfg.get_vect3d ("screen.look_up", se.look_up);
      se.rotate_period = cfg.get_size ("screen.rotate.period");
      cfg.get_vect3d ("screen.rotate.center", se.rotate_center);

      if ( se.rotate_period != 0 )
        {
          vect3d v = se.rotate_center - se.look_from;
          vect3d right = (v % se.look_up).normal();
          se.rotate_normal = (right % v).normal();
        }

      se.normal = se.look_at - se.look_from;
      se.normal.normalize();
      se.right = se.look_up % se.normal;
      se.right.normalize();
      se.up = se.normal % se.right;

      bool zbuffer = cfg.get_bool ("screen.zbuffer");
      se.draw_axes = cfg.get_bool ("screen.draw.axes");
      se.draw_statics = cfg.get_bool ("screen.draw.statics");
      if ( se.fov != 0.0 )
        {
          prec_t phi = 2.0 * PREC_PI * se.fov / 360;
          se.hscale = 2.0 * tan(phi*0.5);
        }
      se.screen = new Screen (w, h, se.hscale, se.hscale * h / w);
      if ( zbuffer )
          se.screen->set_zbuffer (true);
      if ( se.fov != 0.0 )
        {
          se.screen->set_axis (se.look_from, se.right, se.up);
          se.screen->set_perspective (true);
        }
      else
        {
          se.screen->set_axis (se.look_at, se.right, se.up);
        }
      if ( se.draw_axes )
          se.screen->draw_axes ();
      if ( se.draw_statics )
          statics.draw (*se.screen, Screen::BLACK);
      se.pathlimit = cfg.get_size ("screen.pathlimit");
      se.flushlimit = cfg.get_size ("screen.flushlimit");

      se.flushcount = 0;
      se.frameno = 0;

      fprintf (stderr, "Screen '%s':\n", se.path.c_str());
      fprintf (stderr, "  Dimensions: %lux%lu\n", (unsigned long)w, (unsigned long)h);
      char b[128];
      fprintf (stderr, "  HScale: %f\n", prec2double(se.hscale));
      fprintf (stderr, "  ZBuffer: %s\n", zbuffer ? "YES" : "NO");
      fprintf (stderr, "  Draw.Axes: %s\n", se.draw_axes ? "YES" : "NO");
      fprintf (stderr, "  Draw.Statics: %s\n", se.draw_statics ? "YES" : "NO");
      fprintf (stderr, "  Look At: %s\n", se.look_at.sprint (b));
      fprintf (stderr, "  Look From: %s\n", se.look_from.sprint (b));
      fprintf (stderr, "  Look Up: %s\n", se.look_up.sprint (b));
      fprintf (stderr, "  Normal: %s\n", se.normal.sprint (b));
      fprintf (stderr, "  Right: %s\n", se.right.sprint (b));
      fprintf (stderr, "  Up: %s\n", se.up.sprint (b));
      fprintf (stderr, "  Pathlimit: %lu\n", (unsigned long)se.pathlimit);
      fprintf (stderr, "  Flush: %lu\n", (unsigned long)se.flushlimit);
      fprintf (stderr, "  Rotate.Period: %lu\n", (unsigned long)se.rotate_period);
      fprintf (stderr, "  Rotate.Center: %s\n", se.rotate_center.sprint (b));
      fprintf (stderr, "  Rotate.Normal: %s\n", se.rotate_normal.sprint (b));
      if ( se.flushlimit == 0 && se.rotate_period == 0 )
          se.screen->write (se.path.c_str());
    }

  typedef std::list<inject_entry> injects_t;
  injects_t injects;
  for ( i = 0; cfg.select_group ("inject", i); i++ )
    {
      injects.push_back (inject_entry());
      inject_entry& ie = injects.back();
      cfg.get_vect3d ("inject.position", ie.pos);
      cfg.get_vect3d ("inject.direction", ie.dir);
      ie.energy = cfg.get_prec ("inject.energy");

      const char *p = cfg.get_enum ("inject.particle");
      if ( strcmp (p, "electron") == 0 )
        {
          ie.charge = -ELEMENTARY_CHARGE;
          ie.mass = ELECTRON_MASS;
        }
      else
        {
          ie.charge = cfg.get_prec ("inject.charge");
          ie.mass = cfg.get_prec ("inject.mass");
        }

      ie.injections = cfg.get_size ("inject.injections");
      ie.interval = cfg.get_size ("inject.interval");
      ie.spread = cfg.get_prec ("inject.spread");
      ie.count = cfg.get_size ("inject.count");
      ie.start = cfg.get_size ("inject.start");

      cfg.get_vect3d ("inject.coloring.rgb", ie.color_rgb);
      cfg.get_vect3d ("inject.coloring.center", ie.color_center);
      ie.color_radius = cfg.get_prec ("inject.coloring.radius");
      cfg.get_vect3d ("inject.coloring.center.rgb", ie.color_center_rgb);
      cfg.get_vect3d ("inject.coloring.edge.rgb", ie.color_edge_rgb);
      ie.color_new_age = cfg.get_size ("inject.coloring.new.age");
      cfg.get_vect3d ("inject.coloring.new.rgb", ie.color_new_rgb);

      char b[128];
      fprintf (stderr, "Inject at %s:\n", ie.pos.sprint (b));
      fprintf (stderr, "  Charge: %g\n", prec2double(ie.charge));
      fprintf (stderr, "  Mass: %g\n", prec2double(ie.mass));
      fprintf (stderr, "  Energy: %f\n", prec2double(ie.energy));
      fprintf (stderr, "  Direction: %s\n", ie.dir.sprint(b));
      fprintf (stderr, "  Injections: %lu\n", (unsigned long)ie.injections);
      fprintf (stderr, "  Interval: %lu\n", (unsigned long)ie.interval);
      fprintf (stderr, "  Spread: %g\n", prec2double(ie.spread));
      fprintf (stderr, "  Count: %lu\n", (unsigned long)ie.count);
      fprintf (stderr, "  Start: %lu\n", (unsigned long)ie.start);
      fprintf (stderr, "  Color.rgb: %s\n", ie.color_rgb.sprint(b));
      fprintf (stderr, "  Color.center: %s\n", ie.color_center.sprint(b));
      fprintf (stderr, "  Color.radius: %g\n", prec2double(ie.color_radius));
      fprintf (stderr, "  Color.center.rgb: %s\n", ie.color_center_rgb.sprint(b));
      fprintf (stderr, "  Color.edge.rgb: %s\n", ie.color_edge_rgb.sprint(b));
      fprintf (stderr, "  Color.new.age: %lu\n", (unsigned long)ie.color_new_age);
      fprintf (stderr, "  Color.new.rgb: %s\n", ie.color_new_rgb.sprint(b));
    }

  // writing out the povray script, run povray on polywell.pov to get the image
  FILE *fout = fopen ("polywell.inc", "w");
  fprintf (fout, "%s", statics.get_povray_script().c_str());
  fclose (fout);

  dyn.print();

  fprintf (stderr, "Press ENTER to start the simulation.. ");
  getchar();

  prec_t next_sample = dyn.get_elapsed_time() + sample_interval;

  size_t counter = 0;
  size_t samplecounter = 0;
  std::vector<pos_entry> POS;
  POS.reserve (4096);

  time_t last_flush;
  time (&last_flush);

  while ( true )
    {
      prec_t et = dyn.get_elapsed_time();
      if ( et > next_sample )
        {
          next_sample += sample_interval;

          if ( et >= time_to_run )
              break;

          if ( !(counter % 5 ) )
              fprintf (stderr, "\r%.3f%% - %lu         \r", prec2double(100 * et / time_to_run), (unsigned long)dyn.get_ticks());

          size_t dsize = dyn.size();
          POS.resize (dsize);
          for ( size_t i = 0; i < dsize; i++ )
            {
              POS[i].pos = dyn.get_pos(i);
              //printf ("%g\n", prec2double (ElectroDynamics::ms2eV (dyn.get_v(i).length(), ELECTRON_MASS)));
            }

          bool flush_generic = false;
          time_t flush_test;
          time (&flush_test);
          if ( difftime (flush_test, last_flush) >= flush_interval )
            {
              last_flush = flush_test;
              flush_generic = true;
            }

          for ( injects_t::iterator it = injects.begin(); it != injects.end(); it++ )
            {
              inject_entry& ie = *it;
              if ( ie.injections == 0 )
                  continue;
              if ( ie.start == samplecounter )
                {
                  ie.injections--;
                  ie.start += ie.interval;
                  for ( size_t i = 0; i < ie.count; i++ )
                    {
                      vect3d pos = ie.pos;
                      if ( ie.spread != 0 )
                        {
                          pos.x += -ie.spread/2 + prec_t_drand() * ie.spread;
                          pos.y += -ie.spread/2 + prec_t_drand() * ie.spread;
                          pos.z += -ie.spread/2 + prec_t_drand() * ie.spread;
                        }
                      size_t n = dyn.inject_particle (ie.mass, ie.charge, pos, ie.dir, ie.energy);
                      if ( POS.size() <= n )
                          POS.resize (n + 1);
                      POS[n].ie = &ie;
                      POS[n].injected_at = samplecounter;
                    }
                  printf ("Inject! %u left (POS.size=%u)\n", (unsigned int)ie.injections, (unsigned int)POS.size());
                }
            }

          for ( screens_t::iterator st = screens.begin(); st != screens.end(); st++ )
            {
              screen_entry& se = *st;
              screen_entry::pathlogs_t::iterator jt = se.pathlogs.begin();
              for ( size_t i = 0; i < dsize; i++ )
                {
                  if ( jt == se.pathlogs.end() )
                    {
                      se.pathlogs.push_back (screen_entry::pathlog_t());
                      jt = se.pathlogs.end();
                      jt--;
                    }

                  if ( se.pathlimit == 0 && se.rotate_period == 0 )
                    {
                      se.screen->set_pixel (POS[i].pos, get_color (POS[i], samplecounter));
                    }
                  else
                    {
                      jt->push_back (screen_entry::pathentry_t(POS[i].pos, get_color (POS[i], samplecounter)));
                      while ( jt->size() > se.pathlimit )
                          jt->pop_front();
                    }

                  jt++;
                }

              if ( se.flushlimit != 0 )
                  se.flushcount++;

              if ( se.flushlimit == 0 && flush_generic || se.flushlimit && se.flushcount == se.flushlimit )
                {
                  if ( se.pathlimit != 0 || se.rotate_period != 0 )
                    {
                      se.screen->clear();

                      if ( se.rotate_period != 0 )
                        {
                          prec_t a = 2 * PREC_PI * (prec_t)samplecounter/(prec_t)se.rotate_period;
                          vect3d look_from = se.rotate_center + cos(a) * (se.look_from - se.rotate_center) +
                              sin(a) * (se.rotate_normal % (se.look_from - se.rotate_center));

                          vect3d normal = se.look_at - look_from;
                          normal.normalize();
                          vect3d right = se.look_up % normal;
                          right.normalize();
                          vect3d up = normal % right;
                          if ( se.fov != 0.0 )
                              se.screen->set_axis (look_from, right, up);
                          else
                              se.screen->set_axis (se.look_at, right, up);
                        }

                      if ( se.draw_axes )
                          se.screen->draw_axes ();
                      if ( se.draw_statics )
                          statics.draw (*se.screen, Screen::BLACK);
                      screen_entry::pathlogs_t::iterator jt = se.pathlogs.begin();
                      for ( size_t i = 0; i < dsize; i++ )
                        {
                          for ( screen_entry::pathlog_t::iterator kt = jt->begin(); kt != jt->end(); kt++ )
                              se.screen->set_pixel ((*kt).first, (*kt).second);
                          jt++;
                        }
                    }

                  std::string path = se.path.c_str(); // force deep copy
                  char b[32];
                  sprintf (b, "%06lu", (unsigned long)se.frameno);
                  char *x = (char *)strstr (path.c_str(), "XXXXXX");
                  if ( x ) memcpy (x, b, 6);
                  se.screen->write (path.c_str());
#if 0
                  std::string p2 = path.c_str();
                  char *t = (char *)strstr (p2.c_str(), ".bmp");
                  if ( t )
                    {
                      memcpy (t, ".png", 4);
                      char cmd[128];
                      sprintf (cmd, "convert %s %s", path.c_str(), p2.c_str());
                      system (cmd);
                      unlink (path.c_str());
                    }
#endif
                  se.frameno++;
                  se.flushcount = 0;
                }
            }

          samplecounter++;
        }

      dyn.step ();
      counter++;
    }

  for ( screens_t::iterator st = screens.begin(); st != screens.end(); st++ )
    {
      screen_entry& se = *st;
      if ( se.flushlimit == 0 )
        {
          if ( se.pathlimit != 0 || se.rotate_period != 0 )
            {
              se.screen->clear();

              if ( se.rotate_period != 0 )
                {
                  prec_t a = 2 * PREC_PI * (prec_t)samplecounter/(prec_t)se.rotate_period;
                  vect3d look_from = se.rotate_center + cos(a) * (se.look_from - se.rotate_center) +
                      sin(a) * (se.rotate_normal % (se.look_from - se.rotate_center));

                  vect3d normal = se.look_at - look_from;
                  normal.normalize();
                  vect3d right = se.look_up % normal;
                  right.normalize();
                  vect3d up = normal % right;
                  if ( se.fov != 0.0 )
                      se.screen->set_axis (look_from, right, up);
                  else
                      se.screen->set_axis (se.look_at, right, up);
                }

              if ( se.draw_axes )
                  se.screen->draw_axes ();
              if ( se.draw_statics )
                  statics.draw (*se.screen, Screen::BLACK);
              screen_entry::pathlogs_t::iterator jt = se.pathlogs.begin();
              for ( size_t i = 0; i < dyn.size(); i++ )
                {
                  for ( screen_entry::pathlog_t::iterator kt = jt->begin(); kt != jt->end(); kt++ )
                      se.screen->set_pixel ((*kt).first, (*kt).second);
                  jt++;
                }
            }

          std::string path = se.path.c_str(); // force deep copy
          char b[32];
          sprintf (b, "%06lu", (unsigned long)se.frameno);
          char *x = (char *)strstr (path.c_str(), "XXXXXX");
          if ( x ) memcpy (x, b, 6);
          se.frameno++;
          se.flushcount = 0;
          se.screen->write (path.c_str());
        }
    }
  return 0;
}

